<?php

namespace App\Http\Controllers\Api;

use App\Enums\AvailableStatus;
use App\Enums\OrderStatus;
use App\Enums\PaymentStatus;
use App\Enums\Payway;
use App\Enums\ProductStatus;
use App\Enums\ProductType;
use App\Enums\WalletStatus;
use App\Events\OrderPaid;
use App\Exceptions\ApiException;
use App\Http\Controllers\Controller;
use App\Models\AgentConfig;
use App\Models\Asset;
use App\Models\FrontendUser;
use App\Models\Order;
use App\Models\OrderPayment;
use App\Models\Product;
use App\Models\PurchaseWhiteList;
use App\Services\AgentService;
use App\Services\ChargeContext;
use App\Services\OrderService;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;

class OrderController extends Controller
{
    /** @var ChargeContext */
    private $chargeContext;

    /** @var OrderService */
    private $orderService;

    /** @var AgentService */
    private $agentService;

    /**
     * OrderController constructor.
     */
    public function __construct(ChargeContext $chargeContext, OrderService $orderService, AgentService $agentService)
    {
        $this->chargeContext = $chargeContext;
        $this->orderService = $orderService;
        $this->agentService = $agentService;
    }

    /** 下单 */
    public function createOrder()
    {
        request()->validate([
            'product_id' => 'required',
            'quantity' => 'required | integer',
        ]);

        $productId = request()->product_id;
        $quantity = request()->quantity;
        /** @var FrontendUser $user */
        $user = auth()->user();

        $auth = $user->auth;
        if (!$auth) {
            throw new ApiException('请先进行实名认证');
        }
        if ($quantity > 50) {
            throw new ApiException('单次购买数量不能超过50');
        }

        if (1 == $user->punish) {
            throw new ApiException('您已经被限制交易，解除时间' . $user->punish_log->end_time);
        }

        $lock = \Cache::lock('product_order_' . $productId, 5);

        try {
            $lock->block(2); // 如果获取不到锁，则等待2秒
            // 检查是否有未支付订单
            $unPaidOrderCount = Order::query()->where('user_id', auth()->id())
                ->where('agent_id', request()->agent_id)
                ->where('order_status', OrderStatus::NOT_PAY)->count();
            if ($unPaidOrderCount > 0) {
                throw new ApiException('您有未支付的订单，请完成支付或取消后再下单');
            }
            $product = Product::query()->where('agent_id', request()->agent_id)->find($productId);
            if (!$product) {
                throw new ApiException('商品未找到');
            }
            if ($product->status->isNot(ProductStatus::ON_SALE())) {
                throw new ApiException('商品已下架');
            }
            $isResale = $product->type->is(ProductType::RESALE);
            if ($isResale) {
                if ($product->left_count <= 0) {
                    throw new ApiException('转售商品已被锁定，正在支付');
                }
                if ($product->resale_user_id === auth()->id()) {
                    throw new ApiException('不能购买自己发布的商品');
                }
                $userWallet = $user->wallet;
                if (!$userWallet || $userWallet->status->isNot(WalletStatus::OPENED)) {
                    throw new ApiException('未开通钱包，无法购买转售商品');
                }
            }
            if ($quantity > $product->left_count) {
                throw new ApiException('库存不足');
            }
            //检查订单取消数量
            $agentConfigs = $this->agentService->getAgentSettings(
                request()->agent_id,
                [
                    AgentConfig::ORDER_DAILY_CANCEL_LIMIT,
                    AgentConfig::RESALE_ORDER_DAILY_CANCEL_LIMIT,
                ]
            );
            $cancelLimit = Arr::get($agentConfigs, AgentConfig::ORDER_DAILY_CANCEL_LIMIT, 0);
            $resaleCancelLimit = Arr::get($agentConfigs, AgentConfig::RESALE_ORDER_DAILY_CANCEL_LIMIT, 0);
            if (!$isResale && $cancelLimit > 0 && $this->orderService->getUserCancelCount($user->id) >= $cancelLimit) {
                throw new ApiException('发行订单取消次数超出限制，今日禁止下单');
            }
            if ($isResale && $resaleCancelLimit > 0 && $this->orderService->getUserResaleCancelCount($user->id) >= $resaleCancelLimit) {
                throw new ApiException('转售订单取消次数超出限制，今日禁止下单');
            }
            //提前购配置
            $earlyBuy = PurchaseWhiteList::query()
                ->where('product_id', $productId)
                ->where('user_mobile', $user->mobile)
                ->where('status', AvailableStatus::ENABLED)
                ->first();
            // 检查上架时间
            $onlineTime = $product->start_time ?: $product->online_time;
            $startTime = $onlineTime->clone();
            if ($earlyBuy) {
                $startTime = $startTime->subMinutes($earlyBuy->early_buy_minutes);
            }
            if ($startTime && $startTime->gt(now())) {
                throw new ApiException('未到开售时间');
            }
            if ($product->end_time && $product->end_time->lt(now())) {
                throw new ApiException('商品已经停止销售');
            }
            // 检查限购数量
            if ($earlyBuy && now()->lt($onlineTime)) {
                $buyLimit = $earlyBuy->buy_limit;
                $productOrderCount = $this->getMyOrderCount($productId, $earlyBuy->created_at);
            } else {
                $buyLimit = $product->buy_limit;
                $productOrderCount = $this->getMyOrderCount($productId, $onlineTime);
            }
            if (-1 != $buyLimit && $productOrderCount + $quantity > $buyLimit) {
                throw new ApiException('超出限购数量');
            }

            // 创建订单
            $order = new Order();
            $order->agent_id = request()->agent_id;
            $order->user_id = $user->id;
            $order->order_no = Order::generateOrderNo($product->type);
            $order->product_id = $productId;
            $order->product_name = $product->name;
            $order->product_logo_url = $product->logo_url;
            $order->quantity = $quantity;
            $order->sale_price = $product->price;
            $order->order_amount = $product->price * $quantity;
            $order->is_resale = $isResale;
            $order->order_status = OrderStatus::NOT_PAY;

            \DB::transaction(function () use ($product, $order) {
                $order->save();

                // 库存更新
                $product->decrement('left_count', $order->quantity);
                $product->increment('sales_count', $order->quantity);
            });
            # 结束下单
            return ['order_id' => $order->id];
        } catch (LockTimeoutException $e) {
            throw new ApiException('同时下单人数过多，请稍后再试');
        } finally {
            $lock->release();
        }
    }

    /** 查询我的订单 */
    public function listMyOrders()
    {
        $page = request()->page ?: 1;
        $limit = request()->limit ?: 10;

        $query = $this->getMyOrderQuery()
            ->with(['product'])
            ->when(request()->status, function ($q) {
                $statusArr = explode(',', request()->status);

                return $q->whereIn('order_status', $statusArr);
            })
            ->when(request()->product_type, function ($q) {
                return $q->whereHas('product', function ($q1) {
                    return $q1->where('type', request()->product_type);
                });
            })
            ->when(request()->keyword, function ($q) {
                $likeVal = '%' . request()->keyword . '%';

                return $q->where('product_name', 'like', $likeVal)->orWhere('order_no', 'like', $likeVal);
            });

        $count = $query->count();
        $orderLockMinutes = AgentConfig::getConfig(request()->agent_id, AgentConfig::ORDER_LOCK_MINUTES, 10);
        $list = $query
            ->orderByDesc('id')
            ->offset(($page - 1) * $limit)->limit($limit)
            ->get()->map(function (Order $order) use ($orderLockMinutes) {
                $arr = [
                    'id' => $order->id,
                    'order_no' => $order->order_no,
                    'product_name' => $order->product_name,
                    'product_type' => $order->product ? $order->product->type->value : '',
                    'product_logo_url' => $order->product_logo_url,
                    'sale_price' => $order->display_sale_price,
                    'quantity' => $order->quantity,
                    'order_amount' => $order->display_order_amount,
                    'status' => $order->order_status->value,
                    'create_time' => $order->created_at->toDateTimeString(),
                ];
                if ($order->order_status->is(OrderStatus::NOT_PAY())) {
                    $arr['auto_cancel_time'] = $this->getAutoCancelTime($order, $orderLockMinutes)->toDateTimeString();
                }
                $payment = $order->payment;
                if ($order->order_status->is(OrderStatus::PAID) && $payment) {
                    $arr = array_merge($arr, [
                        'pay_time' => $payment->pay_time,
                        'trans_status' => $order->trans_status ? $order->trans_status->value : '',
                        'trans_failed_reason' => $order->trans_fail_reason,
                    ]);
                }

                return $arr;
            });

        return [
            'list' => $list,
            'count' => $count,
        ];
    }

    /** 查询订单详情 */
    public function findOrder($id)
    {
        $order = $this->findMyOrderOrFail($id);
        $arr = [
            'id' => $order->id,
            'product_id' => $order->product_id,
            'order_no' => $order->order_no,
            'product_name' => $order->product_name,
            'product_type' => $order->product->type->value,
            'product_logo_url' => $order->product_logo_url,
            'sale_price' => $order->display_sale_price,
            'quantity' => $order->quantity,
            'order_amount' => $order->display_order_amount,
            'status' => $order->order_status->value,
            'create_time' => $order->created_at->toDateTimeString(),
        ];

        if ($order->order_status->is(OrderStatus::NOT_PAY())) {
            $arr['auto_cancel_time'] = $this->getAutoCancelTime($order)->toDateTimeString();
        }
        if ($order->order_status->is(OrderStatus::PAID)) {
            $payment = $order->payment;
            $arr = array_merge($arr, [
                'pay_time' => $payment->pay_time,
                'pay_way' => $payment->pay_way->value,
                'trans_status' => $order->trans_status ? $order->trans_status->value : '',
                'trans_failed_reason' => $order->trans_fail_reason,
            ]);
        }

        return $arr;
    }

    public function listOrderAssets($orderId)
    {
        $order = $this->findMyOrderOrFail($orderId);

        if (!$order->assets->isempty()) {
            return $order->assets->map(function (Asset $asset) {
                $product = Product::query()->where('contract_address', $asset->contract_address)->first();

                return [
                    'product_id' => $product->id,
                    'product_name' => $product->name,
                    'product_logo_url' => $product->logo_url,
                    'token_id' => $asset->token_id,
                    'cert_sn' => $asset->cert_sn,
                    'trans_hash' => $asset->trans_hash,
                ];
            });
        } else {
            $product = $order->product;
            $product->blind_flag = 1;

            return $product;
        }
    }

    /** 取消订单 */
    public function cancelOrder($id)
    {
        $order = $this->findMyOrderOrFail($id);
        if ($order->order_status->isNot(OrderStatus::NOT_PAY())) {
            throw new ApiException('只能取消未支付的订单');
        }
        $payment = $order->payment;
        if ($payment) {
            $this->chargeContext->initInstance(request()->agent_id);

            $orderInfo = $this->chargeContext->queryOrder($payment->out_order_no);
            $orderStatus = Arr::get($orderInfo, 'status');

            if ('SUCCESS' === $orderStatus) {
                throw new ApiException('订单已支付成功，请耐心等待状态同步');
            }
            if ('PROCESSING' === $orderStatus) {
                $this->chargeContext->closeOrder($payment->out_order_no, Arr::get($orderInfo, 'uniqueOrderNo'));
            }
        }
        \DB::transaction(function () use ($order) {
            $product = $order->product;
            $product->decrement('sales_count', $order->quantity);
            $product->increment('left_count', $order->quantity);

            $order->order_status = OrderStatus::CANCEL;
            $order->save();

            $this->orderService->increaseCancelCount($order->user_id, $order->is_resale);
        });
    }

    /** 删除订单 */
    public function deleteOrder($id)
    {
        $order = $this->findMyOrderOrFail($id);
        if ($order->order_status->in([OrderStatus::CANCEL(), OrderStatus::AUTO_CANCEL()])) {
            throw new ApiException('只能删除已取消的订单');
        }
        $order->delete();
    }

    /** 查询支付参数 */
    public function getPaymentParams($id)
    {
        $payway = Payway::UNION_PAY;
        /** @var Order $order */
        $order = $this->findMyOrderOrFail($id);
        if ($order->order_status->isNot(OrderStatus::NOT_PAY())) {
            throw new ApiException('当前订单不是未支付状态');
        }
        if ($order->payment && !empty($order->payment->pay_link)) {
            return [
                'pay_link' => $order->payment->pay_link,
            ];
        }

        $outOrderNo = strtoupper($payway) . '_' . $order->order_no;
        $order->payment()->delete();

        $payment = new OrderPayment();
        $payment->order_id = $id;
        $payment->merchant_no = '-';
        $payment->out_order_no = $outOrderNo;
        $payment->pay_amount = $order->order_amount;
        $payment->pay_way = $payway;
        $payment->pay_status = PaymentStatus::PAYING;
        $payment->save();

        $product = $order->product;
        $payExtra = [];
        if ($product->type->is(ProductType::RESALE)) {
            $resaleUser = $product->resale_user;
            $resaleUserWallet = $resaleUser->wallet;
            if (!$resaleUserWallet || $resaleUserWallet->status->isNot(WalletStatus::OPENED)) {
                throw new ApiException('转售商品出售方未开通钱包');
            }
            $payExtra['resale_user_wallet_no'] = $resaleUserWallet->wallet_user_no;
            $resaleServiceRate = AgentConfig::getConfig(request()->agent_id, AgentConfig::PAY_RESALE_AGENT_FEE_RATE);
            $payExtra['resale_service_fee'] = toYuan($payment->pay_amount * ($resaleServiceRate / 100));
        }

        $redirectUrl = request()->redirect_url;
        $walletUserNo = '';
        $wallet = $order->user->wallet;
        if ($wallet && $wallet->status->is(WalletStatus::OPENED)) {
            $walletUserNo = $wallet->wallet_user_no;
        }

        $this->chargeContext->initInstance(request()->agent_id);
        $payLink = $this->chargeContext->getUnionPayLink(
            $payment->out_order_no,
            toYuan($payment->pay_amount),
            $order->product_name,
            md5($order->user->mobile),
            $walletUserNo,
            $redirectUrl,
            $this->getAutoCancelTime($order)->subSeconds(30),
            $payExtra
        );

        $payment->pay_link = $payLink;
        $payment->save();

        return [
            'pay_link' => $payLink,
        ];
    }

    /** 测试支付，仅在debug模式下有用 */
    public function testPay()
    {
        request()->validate([
            'order_id' => 'required',
        ]);
        if (!config('app.debug')) {
            throw new ApiException('该接口仅在debug模式下可用');
        }
        $order = Order::query()->where('id', request()->order_id)->first();
        if (!$order) {
            throw new ApiException('无效的订单ID');
        }

        \DB::transaction(function () use ($order) {
            if ($order->order_status->is(OrderStatus::NOT_PAY())) {
                $order->order_status = OrderStatus::PAID;
                $order->save();
                OrderPaid::dispatch($order);
            }
        });
    }

    /** 查询我的订单数量 */
    private function getMyOrderCount($productId, $createAfter)
    {
        return Order::query()->where('user_id', auth()->id())
            ->where('product_id', $productId)
            ->where('created_at', '>=', $createAfter)
            ->whereNotIn('order_status', [OrderStatus::CANCEL, OrderStatus::AUTO_CANCEL])
            ->sum('quantity');
    }

    private function getMyOrderQuery()
    {
        return Order::query()
            ->where('agent_id', request()->agent_id)
            ->where('user_id', auth()->id());
    }

    private function findMyOrderOrFail($id)
    {
        $order = $this->getMyOrderQuery()->find($id);
        if (!$order) {
            throw new ApiException('订单未找到');
        }

        return $order;
    }

    /**
     * @param int $orderLockMinutes
     *
     * @return Carbon
     */
    private function getAutoCancelTime(Order $order, $orderLockMinutes = null)
    {
        if ($order->manual_lock_until) {
            return Carbon::parse($order->manual_lock_until);
        }
        if (!$orderLockMinutes) {
            $orderLockMinutes = AgentConfig::getConfig(request()->agent_id, AgentConfig::ORDER_LOCK_MINUTES, 10);
        }

        return $order->created_at->addMinutes($orderLockMinutes);
    }
}
